package com.thoughtworks.android.androsphere.service.c2dm;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.thoughtworks.android.androsphere.dao.ClientRegistrationDao;
import com.thoughtworks.android.androsphere.service.GoogleServiceConstants;
import com.thoughtworks.android.androsphere.service.auth.ClientLoginAuthService;

public class C2DMProcessor {
    private static final Logger logger = Logger.getLogger(C2DMProcessor.class);

    private ClientLoginAuthService clientLoginAuthService;
    private C2DMServiceHelper helper;
    private ClientRegistrationDao clientRegistrationDao;
    private C2DMHttpRequestHelper c2DMHttpRequestHelper;

    private String authToken;
    private Long noOfRetryFailureAttempts;
    private Map<String, String> postDataMap;

    private C2DMResponse c2DMResponse = C2DMResponse.PROCEED;

    private HashMap<Integer, ResponseCallBack> callbackMap;

    public C2DMProcessor(ClientLoginAuthService clientLoginAuthService, C2DMServiceHelper helper,
                         ClientRegistrationDao clientRegistrationDao, C2DMHttpRequestHelper c2DMHttpRequestHelper) {
        this.clientLoginAuthService = clientLoginAuthService;
        this.helper = helper;
        this.clientRegistrationDao = clientRegistrationDao;
        this.c2DMHttpRequestHelper = c2DMHttpRequestHelper;

        this.authToken = clientLoginAuthService.getAuthToken(false);
        createCallbackMap();
    }

    public C2DMResponse setRequestHeaderAndPostData(Map<String, String> postDataMap) {
        logger.debug("Inside C2DMProcessor : post data map" + postDataMap);
        this.postDataMap = postDataMap;

        if (clientRegistrationDao.getByRegistrationId(getRegistrationId()) != null) {
            this.noOfRetryFailureAttempts = 0l;
            setRequestHeaderAndPostData();
        }
        logger.debug("Outside If condition");
        return c2DMResponse;
    }

    public void waitAndRetry() {
        helper.delayCurrentThreadBy(10l);
        setRequestHeaderAndPostData();
    }

    public void removeRegistrationId() {
        final String registrationId = getRegistrationId();
        clientRegistrationDao.deleteByRegistrationId(registrationId);
    }

    private String getRegistrationId() {
        return postDataMap.get(GoogleServiceConstants.C2DM_REGISTRATION_ID);
    }

    private void setRequestHeaderAndPostData() {
        logger.debug("Inside setRequestHeaderAndPostData");
        c2DMHttpRequestHelper.execute(authToken, postDataMap, new C2DMServiceResponseHandler(callbackMap));
    }

    private void createCallbackMap() {
        callbackMap = new HashMap<Integer, ResponseCallBack>();

        callbackMap.put(200, new ResponseCallBack() {
            public void execute(Map<String, String> headers, InputStream inputStream) {
                logger.debug("C2DM REsponse 200");
                final Map<String, C2DMErrorResponseEnum> responseMap = helper.getResponseMap(inputStream);

                final C2DMErrorResponseEnum errorResponse = responseMap.get("Error");
                if (errorResponse == null) {
                    return;
                }
                c2DMResponse = errorResponse.process(C2DMProcessor.this);
            }
        });

        callbackMap.put(401, new ResponseCallBack() {
            public void execute(Map<String, String> headers, InputStream inputStream) {
                logger.debug("C2DM REsponse 401");
                logger.debug("Re-generating Auth Token");
                authToken = clientLoginAuthService.getAuthToken(true);
                logger.debug("Re-generated Auth Token:" + authToken);

                setRequestHeaderAndPostData();
            }
        });

        callbackMap.put(503, new ResponseCallBack() {
            public void execute(Map<String, String> headers, InputStream inputStream) {
                logger.debug("C2DM REsponse 503");
                noOfRetryFailureAttempts++;

                final String retryAfter = headers.get("Retry-After");
                Long delay;

                if (retryAfter == null || retryAfter.equals("")) {
                    delay = helper.computeTimeForExponentiallyBackingOff(noOfRetryFailureAttempts);
                } else {
                    delay = Long.parseLong(retryAfter) * 10;
                }

                helper.delayCurrentThreadBy(delay);
                setRequestHeaderAndPostData();
            }
        });

        callbackMap.put(405, new ResponseCallBack() {
            public void execute(Map<String, String> headers, InputStream inputStream) {
                logger.debug("C2DM REsponse 405");
            }
        });
    }
}
